from abc import ABCMeta, abstractmethod, ABC


class OrderDetail(object):

    def __init__(self, goods_name, goods_price, quantity) -> None:
        self.goods_name = goods_name
        self.goods_price = goods_price
        self.quantity = quantity

    #  总共价格
    def total_price(self):
        return self.goods_price * self.quantity


class Order(object):

    def __init__(self, user_info, car_list, promotion_type) -> None:
        # 用户信息
        self.user_info = user_info
        # 购物车
        self.car_list = car_list
        # 优惠规则
        self.promotion_type = promotion_type

    def create_order(self):
        factory = PromotionFactory(promotion_type=self.promotion_type)
        det = self.total_order_price() - factory.get_promotion().get_promotion_info(self)
        print(f"实际支付:{self.total_order_price() - det}元,优惠:{det}元")

    def total_order_price(self):
        return sum(car.total_price() for car in self.car_list)

    def total_goods_num(self):
        return sum(car.quantity for car in self.car_list)


# 获取策略工厂类+单例模式
class PromotionFactory(object):
    __instance = None

    def __init__(self, promotion_type=1) -> None:
        self.promotion_type = promotion_type

    def get_promotion(self):
        if isinstance(self.promotion_type, int):
            if self.promotion_type == 1:
                return FidelityPromo()
            elif self.promotion_type == 2:
                return BulkItemPromo()

    # 重写new方法，是为了完成单例模式中的对象地址唯一
    def __new__(cls, *args, **kwargs):
        # 判断是否通过这个类创建过对象
        if not cls.__instance:  # 如果not instance（即非None）
            # 创建对象保存起来
            cls.__instance = object.__new__(cls)  # 给instance赋值
        return cls.__instance


class Promotion(metaclass=ABCMeta):

    @abstractmethod
    def get_promotion_info(self, order):
        return


# 第一个优惠策略
class FidelityPromo(Promotion, ABC):

    def get_promotion_info(self, order):
        total = order.total_order_price()
        return (1000 + (
                total - 1000) * 0.8) if total > 1000 else total


# 第二个优惠策略
class BulkItemPromo(Promotion, ABC):

    def get_promotion_info(self, order):
        total = order.total_goods_num()
        total_money = order.total_order_price()
        return (1000 + (
                total_money - 1000) * 0.7) if total > 20 else total_money


if __name__ == '__main__':
    car_list = [OrderDetail("笔记本电脑", 6300, 1), OrderDetail("华为Mate40", 7800, 1),
                OrderDetail("山西红富士", 120, 20)]

    user_info = {'name': 'Xue', 'user_id': '1', 'address': '蛇皮大道'}
    # 采用策略一优惠
    order_1 = Order(user_info=user_info, car_list=car_list, promotion_type=1)
    # 采用策略二优惠
    order_2 = Order(user_info=user_info, car_list=car_list, promotion_type=2)
    # 创建订单
    order_1.create_order()
    order_2.create_order()
